package track

import (
	"encoding/json"
	"fmt"
	"math"
	"reflect"
	"strconv"
	"strings"
)

type FormatContext struct {
	Remote    bool
	MediaType MediaType
	Direction Direction

	MediaInfo   MediaInfo
	SessionInfo SessionInfo
}

func (f *FormatContext) GetRtpMap() *RtpMapInfo {
	return &f.MediaInfo.RtpMap
}

func (f *FormatContext) GetFMTP() *FmtpInfo {
	return &f.MediaInfo.Fmtp
}

func (f *FormatContext) String() string {
	data, _ := json.MarshalIndent(f, "", "  ")
	return string(data)
}

func (f *FormatContext) Update(v *FormatContext) *FormatContext {
	*f = *v

	return f
}

type RtpMapInfo struct {
	Source    string
	codecType string

	PayloadType uint8
	Codec       CodecId
	ClockRate   int
	Parameters  int
}

func ParseRTPMAP(rtpmap string) (*RtpMapInfo, error) {
	// a=rtpmap:<payload type> <encoding name>/<clock rate>[/<encoding parameters>]
	split := strings.SplitN(rtpmap, " ", 2)
	if len(split) != 2 {
		return nil, fmt.Errorf("error data: %v", rtpmap)
	}

	var c = &RtpMapInfo{
		Source: "rtpmap:" + rtpmap,
	}

	pt, err := strconv.Atoi(split[0])
	if err != nil {
		return nil, fmt.Errorf("error data: %v", rtpmap)
	}
	c.PayloadType = uint8(pt)

	// <encoding name>/<clock rate>[/<encoding parameters>]
	parts := strings.Split(split[1], "/")
	count := len(parts)
	if count >= 1 {
		c.codecType = parts[0]

		if count >= 2 {
			if c.ClockRate, err = strconv.Atoi(parts[1]); err != nil {
				return nil, fmt.Errorf("error data: %v", rtpmap)
			}

			if count >= 3 {
				c.Parameters, _ = strconv.Atoi(parts[2])
			}
		}

	}

	c.Codec = c.parseCodec()

	return c, nil
}

func (c *RtpMapInfo) parseCodec() CodecId {
	codecStr := strings.ToUpper(c.codecType)
	codec := CodecId(codecStr)
	switch codec {
	case CodecIdH264,
		CodecIdH265,
		CodecIdOPUS,
		CodecIdG711A,
		CodecIdG711U,
		CodecIdPS,
		CodecIdTS,
		CodecIdFile,
		CodecIdTSP:
		return codec
	case CodecIdAAC, "AAC":
		return CodecIdAAC
	}

	if strings.Contains(codecStr, "G726") {
		return CodecIdG726
	}

	return CodecId(c.codecType)
}

func (c *RtpMapInfo) GetCodec() CodecId {
	return c.Codec
}

func (c *RtpMapInfo) GetSourceCodec() string {
	return c.codecType
}

type FmtpInfo struct {
	Source string
	KV     map[string]string
}

func ParseFmtp(fmtp string) *FmtpInfo {
	var out = &FmtpInfo{
		Source: "fmtp:" + fmtp,
		KV:     map[string]string{},
	}

	parts := strings.SplitN(fmtp, " ", 2)
	if len(parts) != 2 {
		return out
	}

	parts = strings.Split(parts[1], ";")
	for _, kv := range parts {
		kv = strings.Trim(kv, " ")

		if len(kv) == 0 {
			continue
		}

		keyValue := strings.SplitN(kv, "=", 2)
		if len(keyValue) != 2 {
			continue
		}

		out.KV[strings.ToLower(keyValue[0])] = keyValue[1]
	}

	return out
}

type SessionInfo struct {
	URI       string
	StartTime uint64
	StopTime  uint64

	JsonInfo string

	Information string
	SessionName string

	KV map[string]string
}

func (tis *SessionInfo) Valid() bool {
	return reflect.DeepEqual(*tis, SessionInfo{})
}

type MediaInfo struct {
	RtpMap RtpMapInfo
	Fmtp   FmtpInfo

	SSRC        uint32
	BVSSRC      uint32
	Information string

	MediaProto []string //  RTP/AVP TCP/RTP/AVP WSBV/RTP/SAVPF
	KV         map[string]string
}

func ParseSSRC(ssrc string) (uint32, error) {
	parts := strings.SplitN(ssrc, " ", 2)
	if len(parts) <= 0 {
		return 0, fmt.Errorf("error ssrc. %v", ssrc)
	}

	value, err := strconv.ParseUint(parts[0], 10, 0)
	if err != nil {
		return 0, err
	}

	if value > math.MaxUint32 {
		return 0, fmt.Errorf("error ssrc. %v", ssrc)
	}

	return uint32(value), nil
}

// NetworkType represents the type of network
type NetworkType int

const (
	// NetworkTypeUDP4 indicates UDP over IPv4.
	NetworkTypeUDP4 NetworkType = iota + 1

	// NetworkTypeUDP6 indicates UDP over IPv6.
	NetworkTypeUDP6

	// NetworkTypeTCP4 indicates TCP over IPv4.
	NetworkTypeTCP4

	// NetworkTypeTCP6 indicates TCP over IPv6.
	NetworkTypeTCP6
)

type Candidate struct {
	Type NetworkType // 地址类型, TCP4/UDP4/TCP6/UDP6
	Host string      // 主机(192.168.2.3)
	Port int         // 端口(8520)
}
